package com.nulldev.util.concurrency.threadIt.v3.executors;

import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Deprecated
public class LimitThreadPool extends ThreadPoolExecutor {
	private volatile Object lock = new Object();
	private volatile int count = 0;

	protected static class BlockUntilFree implements RejectedExecutionHandler {
		@Override
		public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
			// Access to the task queue is intended primarily for
			// debugging and monitoring. This queue may be in active
			// use.
			//
			// So we are a little bit off road here :) But since we have
			// full control over executor we are safe.
			try {
				executor.getQueue().put(r);
			} catch (InterruptedException e) {
				// since we cound not rethrow interrupted exception. mark thread
				// as interrupted. and check thread status later in
				// blockExecute()
				Thread.currentThread().interrupt();
			}
		}
	}

	protected static class SafetyCheck implements Runnable {
		Runnable r;

		public SafetyCheck(Runnable r) {
			this.r = r;
		}

		public void run() {
			throw new RuntimeException("should never call run() on this class");
		}

		public Runnable getCause() {
			return r;
		}
	}

	public LimitThreadPool(int maxThreadCount) {
		super(0, maxThreadCount, 0, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), new ThreadFactory() {

			@Override
			public Thread newThread(Runnable r) {
				return new Thread(r, "LTP: Executor");
			}
		}, new BlockUntilFree());
	}

	protected void beforeExecute(Thread t, Runnable r) {
		synchronized (lock) {
			count++;
		}

		super.beforeExecute(t, r);
	}

	@Override
	protected void afterExecute(Runnable r, Throwable t) {
		super.afterExecute(r, t);

		synchronized (lock) {
			count--;

			lock.notifyAll();
		}
	}

	/**
	 * downloader working if here any getTasks() > 0
	 */
	public boolean active() {
		synchronized (lock) {
			return count > 0;
		}
	}

	/**
	 * Wait until current task ends. if here is no tasks exit immidiatly.
	 *
	 * @throws InterruptedException
	 *
	 */
	public void waitUntilNextTaskEnds() throws InterruptedException {
		synchronized (lock) {
			if (active()) {
				lock.wait();
			}
		}
	}

	/**
	 * Wait until thread pool execute its last task. Waits forever unti end.
	 *
	 * @throws InterruptedException
	 *
	 */
	public void waitUntilTermination() throws InterruptedException {
		synchronized (lock) {
			while (active())
				waitUntilNextTaskEnds();
		}
	}

	/**
	 * You should not call this method on this Limited Version Thread Pool. Use
	 * blockExecute() instead.
	 */
	@Override
	public void execute(final Runnable command) {
		if (!(command instanceof SafetyCheck)) {
			super.execute(command);
		} else {
			final SafetyCheck s = (SafetyCheck) command;
			super.execute(s.getCause());
		}

	}

	public void blockExecute(Runnable command) throws InterruptedException {
		execute(new SafetyCheck(command));

		if (Thread.interrupted())
			throw new InterruptedException();
	}

	public void awaitIdle() {
		final int limit = 8;
		int waitingInt = limit;
		final int s = (int) (35 / limit);
		while (!this.active()) {
			try {
				if (waitingInt <= 0) {
					waitingInt = limit + 1;
				}
				Thread.sleep((long) Math.ceil(s / waitingInt));
				waitingInt -= 1;
			} catch (InterruptedException e) {
			}
		}
	}
}
